package XMFramework.Core
{
	import GTweener.TweenLite;
	
	import XMFramework.Interface.IModule;
	
	import flash.display.Loader;
	import flash.events.Event;
	import flash.events.HTTPStatusEvent;
	import flash.events.IEventDispatcher;
	import flash.events.IOErrorEvent;
	import flash.events.ProgressEvent;
	import flash.events.SecurityErrorEvent;
	import flash.net.URLLoader;
	import flash.net.URLRequest;
	import flash.system.ApplicationDomain;
	import flash.system.LoaderContext;
	import flash.utils.setTimeout;

	public class SModuleDomain
	{
		private var onModuleCompleteFunc:Function;
		private var onResultFunc:Function;
		
		// Some module may be store in the same application domain 
		// Object mark which remote application domain is loaded or not
		private var appDomainList:Object	=	new Object();	
		
		internal var modulesList:Array;
		internal var preloadModules:Array;
		internal var isInitComplete:Boolean		=	false;
		
		internal function loadModuleList( hostPath:String, components:Object, onResult:Function, onStepLoad:Function, onModuleComplete:Function ):void			
		{			
			onModuleCompleteFunc	=	onModuleComplete;
			onResultFunc			=	onResult;
			
			modulesList				=	new Array();
			
			// Force to start download all module
			for( var i:String in components )
			{				
				var mods : Object	=	new Object();
				mods.info			=	components[i];
				mods.loaderURL		=	hostPath + mods.info.url;
				mods.totalByte 		=	0;
				mods.loadedByte		=	0;
				mods.handle			=	null;
				modulesList.push( mods );
				if( ApplicationDomain.currentDomain.hasDefinition( components[i].module ) )
					initModules( mods, ApplicationDomain.currentDomain );
								
				else if( appDomainList.hasOwnProperty( mods.info.url ) )
					mods.loader	=	appDomainList[mods.info.url];
						
				else 
					appDomainList[mods.info.url]	=	downloadAppDomain( hostPath, mods, onStepLoad );					
			}
		}
		
		internal function calResourcesTotalByte():int
		{
			var totalByte:int	=	0;
			for( var i:String in modulesList )
				totalByte	+=	modulesList[i].totalByte;
			
			if( totalByte == 0 )
				return int.MAX_VALUE;
			
			return totalByte;
		}
		
		internal function calResourcesLoadedByte():int
		{
			var loadedByte:int	=	0;
			for( var i:String in modulesList )
				loadedByte	+=	modulesList[i].loadedByte;
			
			return loadedByte;
		}
		
		private function downloadAppDomain( hostPath:String, mdInfo:Object, onStepLoad:Function ) : Loader
		{
			var context:LoaderContext	=	new LoaderContext();
			context.applicationDomain	=	ApplicationDomain.currentDomain;	// Loader's own ApplicationDomain
			//context.applicationDomain	=	new ApplicationDomain( ApplicationDomain.currentDomain );	// Child of loader's ApplicationDomain
			context.securityDomain		=	null;
			
			mdInfo.loader				=	new Loader();
			mdInfo.loader.addEventListener( SecurityErrorEvent.SECURITY_ERROR, onModuleLoadError );
			
			var dispatcher : IEventDispatcher	=	mdInfo.loader.contentLoaderInfo;
			dispatcher.addEventListener( Event.COMPLETE, onModuleLoadComplete );
			dispatcher.addEventListener( IOErrorEvent.IO_ERROR, onModuleLoadError );
			dispatcher.addEventListener( SecurityErrorEvent.SECURITY_ERROR, onModuleLoadSecurity );
			dispatcher.addEventListener( ProgressEvent.PROGRESS, onLoadPercentage );
			
			mdInfo.loader.load( new URLRequest( hostPath + mdInfo.info.url ), context );			
			
			trace( "SModuleDomain::downloadAppDomain started: " + mdInfo.info.module + " at " + mdInfo.info.url );
			
			return mdInfo.loader;
			
			function onModuleLoadSecurity( evt:SecurityErrorEvent ) : void
			{
				trace( "SModuleDomain::onModuleLoadError security error: " + evt.text );
				
				mdInfo.loader.unload();
				mdInfo.loader	=	null;
				setTimeout( downloadAppDomain, 1, hostPath, mdInfo, onStepLoad );
			}
			
			function onModuleLoadError( evt:IOErrorEvent ) : void
			{
				trace( "SModuleDomain::onModuleLoadError failure: " + evt.text );
				
				mdInfo.loader.unload();
				mdInfo.loader	=	null;
				setTimeout( downloadAppDomain, 1, hostPath, mdInfo, onStepLoad );
			}
			
			function onLoadPercentage( evt:ProgressEvent ) : void
			{
				mdInfo.totalByte 	=	evt.bytesTotal;
				mdInfo.loadedByte	=	evt.bytesLoaded;
				
				if( onStepLoad != null )
					onStepLoad.call( null, mdInfo.info.url, evt.bytesTotal, evt.bytesLoaded );
			}
		}		
		
		private function onModuleLoadComplete( evt:Event ) : void
		{
			trace( "SModuleDomain::onModuleLoadComplete " + evt.currentTarget.url + " succeeded" );
			
			// Test to see if all module file is downloaded complete
			isInitComplete	=	true;
			var url:String = String(evt.currentTarget.url).replace("file:///E:/Soft/xampp/htdocs/CGKGame/compile/debug", ".");
			for( var i:String in modulesList )
				if( url == modulesList[i].loaderURL )
					initModules( modulesList[i], evt.currentTarget.applicationDomain );
				
			// Check to see all module is loa7d complete
			for( i in modulesList )
				if( modulesList[i].handle == null )
					isInitComplete	=	false;
			
			// Tell SCore all module part is ready for use
			if( isInitComplete )
				
				// Delay a while for Flash Player load swf completely
				GTweener.TweenLite.delayedCall( 1, function():void{	onModuleCompleteFunc.call( null ); }, null, true );
		}				
		
		private function initModules( mdInfo:Object, appDomain:ApplicationDomain ) : void
		{	
			// Already initialized
			if( mdInfo.handle != null )
				return;
			
			// 
			if( appDomain.hasDefinition( mdInfo.info.module ) )
			{
				// Allocate new module instance
				var mdlClass : Class			=	appDomain.getDefinition( mdInfo.info.module ) as Class;
				var newInstance : IModule		=	new mdlClass();
				
				// Store back module run code
				mdInfo.handle					=	newInstance;
				
				trace( "SModuleDomain::initModules " + mdInfo.info.module+ " succeeded" );
			}
			else
				onResultFunc( SCore.ERROR_INIT_MODULE, "Init module: " + mdInfo.info.module + " at " + mdInfo.info.url + " FAILED." );
		}
	}
}